package uk.co.richardwkirk.funnels.model;
import java.util.LinkedList;

import uk.co.richardwkirk.funnels.FunnelSettings;

public class Funnel {
	
	public class FunnelledRunner {
		public FunnelledRunner(Runner nextRunner, int q) {
			this.runner = nextRunner;
			this.queue = q;
		}
		
		public Runner runner;
		public int queue;
	}
	
	FunnelledRunner[] funnelPlaces;
	int funnelId = -1;
	int runnerCount = 0;
	int numberOfPlaces = 0;
	boolean isShared = false;
	
	public Funnel(int funnelId_, FunnelSettings settings) {
		funnelId = funnelId_;
		numberOfPlaces = (int)(settings.LENGTH / settings.SPACE_SIZE);
		funnelPlaces = new FunnelledRunner[numberOfPlaces];
		for (int i = 0; i < numberOfPlaces; ++i) {
			funnelPlaces[i] = null;
		}
		isShared = settings.SHARED_FUNNELS;
	}

	public String getName() {
		return "F" + funnelId;
	}

	public int getId() {
		return funnelId;
	}
	
	public int getNumberOfRunners() {
		return runnerCount;
	}

	public boolean hasSpace(int numberOfSpaces)
	{
		for (int i = 1; i <= numberOfSpaces; ++i) {
			if (funnelPlaces[numberOfPlaces - i] != null)
			{
				return false;
			}
		}
		return true;
	}
	

	public void add(Runner nextRunner, int queue) throws FullFunnelException {
		if (hasSpace(1)) {
			funnelPlaces[numberOfPlaces - 1] = new Funnel.FunnelledRunner(nextRunner, queue);
			++runnerCount;
		}
		else {
			throw new FullFunnelException("Attempted to put a runner into a full funnel.");
		}
	}
	
	public Runner exit(int drainQueue) {
		Runner exitRunner = null;
		if (funnelPlaces[0] != null)
		{
			if (funnelPlaces[0].queue == drainQueue) {
				exitRunner = funnelPlaces[0].runner;
				funnelPlaces[0] = null;
				--runnerCount;
			}
		}
		return exitRunner;
	}

	public boolean hasAvailableSpace() {
		if (isShared) {
			// Funnel classed as shared so we allow more runners in
			// if there is any space available
			return (funnelPlaces[numberOfPlaces - 1] == null);
		}
		else {
			// Funnel must be empty before more empty
			return (runnerCount == 0);
		}
	}
	
	public boolean isEmpty() {
		return (runnerCount == 0);
	}

	public void shiftUp() {
		for (int i = 0; i < numberOfPlaces - 1; ++i)
		{
			if (funnelPlaces[i] == null)
			{
				funnelPlaces[i] = funnelPlaces[i+1];
				funnelPlaces[i+1] = null;
			}
		}
	}
	
	private FunnelledRunner getFirstRunner() {
		for (int i = 0; i < numberOfPlaces - 1; ++i)
		{
			if (funnelPlaces[i] != null)
			{
				return funnelPlaces[i];
			}
		}
		return null;
	}

	public FunnelledRunner getRunnerAt(int i) {
		return funnelPlaces[i];
	}

	public int getNumberOfPlaces() {
		return numberOfPlaces;
	}

	public boolean isQueueEmpty(int drainQueue) {
		Funnel.FunnelledRunner firstFunnelledRunner = getFirstRunner();
		return (firstFunnelledRunner == null || firstFunnelledRunner.queue != drainQueue);
	}


}
